Example data analysis for manuscript
baRulho:quantifying habitat-induced degradation of (animal) acoustic signals
0.1 Load package
0.2 Synthetize sounds
Create synthetic sounds to be used for making the master sound file for playback experiments:
Code
synth_data <-
synth_sounds(
replicates = 3, # number of replicates for each unique combination of varying features
frequencies = seq(0.5, 10, length.out = 20),
durations = c(0.2, 0.1),
am = TRUE, # amplitude modulation
fm = TRUE, # frequency modulation
sig2 = 0.8, # frequency modulation parameter
shuffle = TRUE # randomize the position of sounds
)The output is of class data frame and extended selection table (warbleR package format, here printed as a data frame):
[1] 68 70 72 119 78 120 86 235 33 93 140 16 80 229 48 375 192 370 [19] 44 9 67 58 127 276 168 227 207 355 225 107 275 406 242 343 224 407 [37] 249 237 387 263 175 117 381 377 295 322 273 368 203 76 122 43 92 52 [55] 55 121 18 277 130 158 139 83 145 155 294 313 202 285 136 126 300 82 [73] 291 233 310 177 111 435 421 328 278 248 239 282 352 408 386 426 213 74 [91] 374 231 409 422 210 240 412 106 380 437 360 357 267 415 262 372 396 379 [109] 400 129 113 64 144 14 99 79 71 236 108 8 6 148 19 73 165 167 [127] 221 264 115 38 178 3 271 284 230 228 132 371 404 369 269 299 162 197 [145] 365 376 425 329 255 152 414 307 402 319 173 303 347 91 429 321 391 327 [163] 287 397 247 433 385 317 417 84 60 45 100 34 134 63 193 125 23 15 [181] 94 53 204 154 344 281 353 138 42 56 85 211 289 201 217 309 434 241 [199] 137 256 392 220 411 214 419 182 358 333 292 250 59 364 174 351 216 362 [217] 293 131 1 27 69 146 7 35 11 194 123 10 65 114 21 36 222 169 [235] 186 279 75 96 180 98 232 286 195 170 135 346 373 324 160 226 254 252 [253] 337 345 367 331 166 95 416 251 315 366 301 270 389 12 403 323 320 427 [271] 290 410 171 388 325 390 420 142 141 62 47 128 29 26 133 185 147 13 [289] 31 118 49 159 190 172 219 302 104 81 208 25 260 297 314 288 149 438 [307] 394 418 296 206 184 238 363 336 354 356 188 57 424 215 423 341 283 274 [325] 378 28 405 326 339 413 304 428 187 361 338 335 430 54 112 150 151 212 [343] 110 109 359 30 306 40 156 89 77 39 102 189 305 124 50 340 234 245 [361] 266 258 198 311 268 348 103 432 265 157 431 399 37 66 196 259 436 223 [379] 398 199 46 349 209 401 280 312 205 32 90 24 105 191 5 22 342 101 [397] 261 87 97 88 143 2 41 20 183 61 4 179 298 51 164 200 161 350 [415] 246 316 153 395 181 257 330 334 318 17 244 176 308 253 116 332 393 383 [433] 243 163 272 384 218 382| sound.files | selec | start | end | bottom.freq | top.freq | frequency | duration | frequency.modulation | amplitude.modulation | treatment | replicate | sound.id |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| synthetic_sound_68 | 1 | 0.05 | 0.25 | 8.896 | 9.584 | 9 | 0.2 | fm | am | dur:0.2;freq:9;fm;am | 1 | dur:0.2;freq:9;fm;am_1 |
| synthetic_sound_70 | 1 | 0.05 | 0.25 | 3.215 | 4.219 | 4 | 0.2 | fm | am | dur:0.2;freq:4;fm;am | 1 | dur:0.2;freq:4;fm;am_1 |
| synthetic_sound_72 | 1 | 0.05 | 0.25 | 0.244 | 1.399 | 1 | 0.2 | fm | am | dur:0.2;freq:1;fm;am | 1 | dur:0.2;freq:1;fm;am_1 |
| synthetic_sound_119 | 1 | 0.05 | 0.25 | 6.209 | 7.184 | 7 | 0.2 | fm | am | dur:0.2;freq:7;fm;am | 1 | dur:0.2;freq:7;fm;am_1 |
| synthetic_sound_78 | 1 | 0.05 | 0.25 | 9.007 | 9.777 | 9.5 | 0.2 | fm | am | dur:0.2;freq:9.5;fm;am | 1 | dur:0.2;freq:9.5;fm;am_1 |
| synthetic_sound_120 | 1 | 0.05 | 0.25 | 3.918 | 4.800 | 4.5 | 0.2 | fm | am | dur:0.2;freq:4.5;fm;am | 1 | dur:0.2;freq:4.5;fm;am_1 |
| synthetic_sound_86 | 1 | 0.05 | 0.25 | 2.899 | 3.773 | 3 | 0.2 | fm | am | dur:0.2;freq:3;fm;am | 1 | dur:0.2;freq:3;fm;am_1 |
| synthetic_sound_78 | 2 | 0.05 | 0.25 | 9.007 | 9.777 | 9.5 | 0.2 | fm | am | dur:0.2;freq:9.5;fm;am | 2 | dur:0.2;freq:9.5;fm;am_2 |
| synthetic_sound_33 | 1 | 0.05 | 0.25 | 0.954 | 2.230 | 2 | 0.2 | fm | am | dur:0.2;freq:2;fm;am | 1 | dur:0.2;freq:2;fm;am_1 |
| synthetic_sound_93 | 1 | 0.05 | 0.25 | 6.006 | 6.790 | 6.5 | 0.2 | fm | am | dur:0.2;freq:6.5;fm;am | 1 | dur:0.2;freq:6.5;fm;am_1 |
1 Create master sound file
This step puts all sounds together into a single sound file:
The output file is saved in the current working directory (can be modified using argument ‘path’). A similar file was used for the playback experiments detailed in the paper. The following section shows how to access the test (re-recorded) files.
These are the annotations for the sounds in the master sound files:
| sound.files | selec | start | end | bottom.freq | top.freq | sound.id |
|---|---|---|---|---|---|---|
| master.wav | 1 | 1.000000 | 2.000000 | 1.333333 | 2.666667 | start_marker |
| master.wav | 2 | 2.050000 | 2.250023 | 7.875000 | 8.805000 | dur:0.2;freq:9;fm;am_1 |
| master.wav | 3 | 2.300023 | 2.500045 | 3.208000 | 4.069000 | dur:0.2;freq:4;fm;am_1 |
| master.wav | 4 | 2.550045 | 2.750068 | 0.422000 | 1.223000 | dur:0.2;freq:1;fm;am_1 |
| master.wav | 5 | 2.800068 | 3.000091 | 6.905000 | 7.917000 | dur:0.2;freq:7;fm;am_1 |
| master.wav | 6 | 3.050091 | 3.250113 | 8.417000 | 9.416000 | dur:0.2;freq:9.5;fm;am_1 |
1.1 Download data
This code downloads the test files. The files were re-recorded during a transmission experiment at 10, 30, 65 and 100 m:
Code
path_to_files <- "PATH_TO_FILES" # add folder path to keep master and test files
# directory path where supplementary files have been saved
options(sound.files.path = path_to_files)
download.file("https://figshare.com/ndownloader/files/41905809", destfile = file.path(path_to_files,
"degrad_exp_files.zip"), method = "wget")
unzip(file.path(path_to_files, "degrad_exp_files.zip"), exdir = file.path(path_to_files))2 Find markers
The code below finds the position of the start and end markers in the test files:
| sound.files | selec | start | end | scores | marker | time.mismatch |
|---|---|---|---|---|---|---|
| trnsc1_100m_closed.wav | 1 | 6.049203 | 7.049203 | 0.2227792 | start_marker | 0.0300544 |
| trnsc1_100m_closed.wav | 2 | 257.700686 | 258.700686 | 0.2726756 | end_marker | NA |
| trnsc1_100m_open.wav | 3 | 29.502300 | 30.502300 | 0.7150524 | start_marker | 0.0131017 |
| trnsc1_100m_open.wav | 4 | 281.136830 | 282.136830 | 0.6083450 | end_marker | NA |
| trnsc1_10m_closed.wav | 5 | 39.452253 | 40.452253 | 0.7046298 | start_marker | 0.0114997 |
| trnsc1_10m_closed.wav | 6 | 291.085182 | 292.085182 | 0.8364759 | end_marker | NA |
The column ‘time.mismatch’ compares the time difference between the two templates on test-files against that in the master sound file. In a perfect marker detection the value must be 0, meaning that the time in between markers in the master and test files is exactly the same. In this case the average mismatch is of 14 ms and the highest of 32 ms:
[1] 0.01438455
[1] 0.03171009
Modifing detection parameters as spectrogram type (‘type’ argument), time window overlap (‘ovlp’ argument) and hop size (‘hop.size’ argument) can be adjusted in order to improve precission. Note that for aligning all other sounds only the marker with the highest correlation will be used. Therefore the time mismatch is likely to be lower in the aligned test sounds.
3 Align sounds
Once we know the position of markers we can compute the position for all other sounds in the test files (i.e. align):
| sound.files | selec | start | end | bottom.freq | top.freq | sound.id | marker |
|---|---|---|---|---|---|---|---|
| trnsc1_100m_closed.wav | 1 | 7.129257 | 7.329280 | 7.875 | 8.805 | dur:0.2;freq:9;fm;am_1 | end_marker |
| trnsc1_100m_closed.wav | 2 | 7.379280 | 7.579303 | 3.208 | 4.069 | dur:0.2;freq:4;fm;am_1 | end_marker |
| trnsc1_100m_closed.wav | 3 | 7.629303 | 7.829325 | 0.422 | 1.223 | dur:0.2;freq:1;fm;am_1 | end_marker |
| trnsc1_100m_closed.wav | 4 | 7.879325 | 8.079348 | 6.905 | 7.917 | dur:0.2;freq:7;fm;am_1 | end_marker |
| trnsc1_100m_closed.wav | 5 | 8.129348 | 8.329371 | 8.417 | 9.416 | dur:0.2;freq:9.5;fm;am_1 | end_marker |
| trnsc1_100m_closed.wav | 6 | 8.379371 | 8.579393 | 4.171 | 4.839 | dur:0.2;freq:4.5;fm;am_1 | end_marker |
This plot shows the aligned re-recorded sounds for transcect 1:
Code
graphics.off()
lf <- rep(c(0.06, 0.5), each = 4)
rg <- rep(c(0.5, 0.95), each = 4)
horiz <- seq(0.95, 0.075, length.out = 5)
btm <- rep(horiz[-1], 2)
tp <- rep(horiz[-length(horiz)], 2)
m <- cbind(lf, rg, btm, tp)
lf <- c(rep(0.95, each = 4), 0.06, 0.5, 0, 0)
rg <- c(rep(1, each = 4), 0.5, 0.95, 0.05, 1)
horiz <- seq(0.95, 0.075, length.out = 5)
btm <- c(horiz[-1], 0.95, 0.95, 0.075, 0)
tp <- c(horiz[-length(horiz)], 1, 1, 0.95, 0.075)
m2 <- cbind(lf, rg, btm, tp)
m <- rbind(m, m2)
for (e in paste0("trnsc", 1:3)) {
png(filename = paste0("./output/spectrograms_by_habitat_and_distance_",
e, ".png"), res = 300, width = 4000, height = 3000)
ss <- split.screen(figs = m)
# # testing layout screens for(i in 1:nrow(m)) {screen(i)
# par( mar = rep(0, 4)) plot(0.5, xlim = c(0,1), ylim =
# c(0,1), type = 'n', axes = FALSE, xlab = '', ylab = '',
# xaxt = 'n', yaxt = 'n') box() text(x = 0.5, y = 0.5,
# labels = i) } close.screen(all.screens = T)
ovlp <- 1
colls <- seq(-110, 0, 5)
wl <- 512
lab_bg <- viridis(10, alpha = 0.25)[8]
lab_bg <- "#3A3B39"
files <- c("_10m_open.wav", "_30m_open.wav", "_65m_open.wav",
"_100m_open.wav", "_10m_closed.wav", "_30m_closed.wav", "_65m_closed.wav",
"_100m_closed.wav")
files <- paste0(e, files)
titles <- gsub(paste0(e, "_|_|.wav"), " ", files)
# frequency label
screen(15)
par(mar = c(0, 0, 0, 0), new = TRUE)
plot(1, frame.plot = FALSE, type = "n", yaxt = "n", xaxt = "n")
text(x = 0.9, y = 1, "Frequency (kHz)", srt = 90, cex = 1.6)
# time label
screen(16)
par(mar = c(0, 0, 0, 0), new = TRUE)
plot(1, frame.plot = FALSE, type = "n", yaxt = "n", xaxt = "n")
text(x = 1, y = 0.75, "Time (s)", cex = 1.6)
for (i in seq_along(files)) {
# print(i)
screen(i)
par(mar = c(0, 0, 0, 0))
warbleR:::spectro_wrblr_int2(wave = readWave(file.path(path_to_files,
files[i]), from = min(aligned_tests$start[aligned_tests$sound.files ==
files[i]]) - 1.2, to = min(aligned_tests$start[aligned_tests$sound.files ==
files[i]]) + 4, units = "seconds"), collevels = colls,
ovlp = ovlp, wl = wl, flim = c(0.1, 10.6), palette = viridis,
axisX = FALSE, axisY = FALSE, grid = FALSE)
# add frequency axis
if (grepl("open", aligned_tests$sound.files[aligned_tests$sound.files ==
files[i]]))
axis(2, at = c(seq(2, 10, 2)))
# add time axis
if (grepl("100m", aligned_tests$sound.files[aligned_tests$sound.files ==
files[i]]))
axis(1)
lns <- c(aligned_tests$start[aligned_tests$sound.files ==
files[i]] - min(aligned_tests$start[aligned_tests$sound.files ==
files[i]]), aligned_tests$end[aligned_tests$sound.files ==
files[i]] - min(aligned_tests$start[aligned_tests$sound.files ==
files[i]])) + 1.2
lns <- c(lns, min(lns) - 0.1, min(lns) - 1.05)
abline(v = lns, col = "white", lty = 3, lwd = 1.2)
abline(v = lns, col = "white", lty = 3, lwd = 1.2)
}
vlabs <- paste(c(10, 30, 65, 100), "m")
par(mar = c(0, 0, 0, 0), bg = lab_bg, new = TRUE)
# add vertical labels
for (i in 9:12) {
screen(i)
# par(mar = c(0, 0, 0, 0))
par(mar = c(0, 0, 0, 0), bg = lab_bg, new = TRUE)
plot(1, frame.plot = FALSE, type = "n", yaxt = "n", xaxt = "n")
text(x = 1, y = 1, vlabs[i - 8], srt = 270, cex = 1.6, col = "white",
font = 2)
box()
}
hlabs <- c("Open habitat", "Closed habitat")
for (i in 13:14) {
screen(i)
par(mar = c(0, 0, 0, 0), bg = lab_bg, new = TRUE)
plot(1, frame.plot = FALSE, type = "n", yaxt = "n", xaxt = "n")
text(x = 1, y = 1, hlabs[i - 12], font = 2, cex = 1.6, col = "white")
box()
}
dev.off()
}4 Measuring degradation
Must degradation metrics involve comparing tests sounds that were recorded at different distances from the speaker, to their reference, which is typically recorded at 1m. Hence, a column indicating the distance at which each sound was recorded is needed. In this case the recording distance can be extracted from the sound file name:
Once the distance is included in the annotations degradation metrics can be obtained with few lines of code. For instance the following code computes excess attenuation, signal-to-noise ratio, blur ratio and tail-to-signal ratio:
| sound.files | selec | start | end | bottom.freq | top.freq | sound.id | marker | distance | reference | excess.attenuation | signal.to.noise.ratio | blur.ratio | tail.to.signal.ratio |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| trnsc1_100m_closed.wav | 1 | 7.129257 | 7.32928 | 7.875 | 8.805 | dur:0.2;freq:9;fm;am_1 | end_marker | 100 | trnsc1_1m_open.wav-1 | -10.934431 | 0.3619688 | 0.2684761 | -0.705663 |
| trnsc1_100m_open.wav | 1 | 30.552300 | 30.75232 | 7.875 | 8.805 | dur:0.2;freq:9;fm;am_1 | start_marker | 100 | trnsc1_1m_open.wav-1 | 16.052347 | 0.7887214 | 0.2148822 | -1.235814 |
| trnsc1_10m_closed.wav | 1 | 40.513753 | 40.71378 | 7.875 | 8.805 | dur:0.2;freq:9;fm;am_1 | end_marker | 10 | trnsc1_1m_open.wav-1 | 4.166308 | 22.8338669 | 0.1168380 | -25.819426 |
| trnsc1_10m_open.wav | 1 | 31.154559 | 31.35458 | 7.875 | 8.805 | dur:0.2;freq:9;fm;am_1 | end_marker | 10 | trnsc1_1m_open.wav-1 | 6.759965 | 23.6130624 | 0.1643497 | -33.034854 |
| trnsc1_1m_open.wav | 1 | 101.175861 | 101.37588 | 7.875 | 8.805 | dur:0.2;freq:9;fm;am_1 | end_marker | 1 | trnsc1_1m_open.wav-1 | NA | 26.9087306 | NA | -43.379831 |
| trnsc1_30m_closed.wav | 1 | 31.784472 | 31.98449 | 7.875 | 8.805 | dur:0.2;freq:9;fm;am_1 | end_marker | 30 | trnsc1_1m_open.wav-1 | 5.981216 | 2.5834933 | 0.1646304 | -3.672095 |
Session information
R version 4.1.0 (2021-05-18)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 20.04.2 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/atlas/libblas.so.3.10.3
LAPACK: /usr/lib/x86_64-linux-gnu/atlas/liblapack.so.3.10.3
locale:
[1] LC_CTYPE=pt_BR.UTF-8 LC_NUMERIC=C
[3] LC_TIME=es_CR.UTF-8 LC_COLLATE=pt_BR.UTF-8
[5] LC_MONETARY=es_CR.UTF-8 LC_MESSAGES=pt_BR.UTF-8
[7] LC_PAPER=es_CR.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=es_CR.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] viridis_0.6.3 viridisLite_0.4.2 tidyr_1.1.3 baRulho_1.1.0
[5] ohun_1.0.0 warbleR_1.1.29 NatureSounds_1.0.4 seewave_2.2.1
[9] tuneR_1.4.4 rprojroot_2.0.3 formatR_1.11 knitr_1.43
[13] kableExtra_1.3.4 remotes_2.4.2
loaded via a namespace (and not attached):
[1] httr_1.4.4 jsonlite_1.8.7 brio_1.1.3 assertthat_0.2.1
[5] yaml_2.3.7 pillar_1.9.0 backports_1.4.1 glue_1.6.2
[9] digest_0.6.33 checkmate_2.2.0 rvest_1.0.3 Sim.DiffProc_4.8
[13] colorspace_2.1-0 htmltools_0.5.5 pkgconfig_2.0.3 purrr_1.0.0
[17] scales_1.2.1 vdiffr_1.0.5 webshot_0.5.4 svglite_2.1.0
[21] dtw_1.23-1 tibble_3.2.1 proxy_0.4-27 generics_0.1.3
[25] ggplot2_3.4.2 pbapply_1.7-2 cli_3.6.1 magrittr_2.0.3
[29] evaluate_0.21 fansi_1.0.4 MASS_7.3-60 xml2_1.3.5
[33] class_7.3-22 tools_4.1.0 lifecycle_1.0.3 stringr_1.5.0
[37] fftw_1.0-7 munsell_0.5.0 compiler_4.1.0 Deriv_4.1.3
[41] e1071_1.7-13 signal_0.7-7 systemfonts_1.0.4 rlang_1.1.1
[45] classInt_0.4-9 units_0.8-2 grid_4.1.0 RCurl_1.98-1.12
[49] rstudioapi_0.14 rjson_0.2.21 htmlwidgets_1.5.4 igraph_1.5.0.1
[53] bitops_1.0-7 rmarkdown_2.23 testthat_3.1.10 gtable_0.3.3
[57] DBI_1.1.3 R6_2.5.1 gridExtra_2.3 dplyr_1.0.10
[61] fastmap_1.1.1 utf8_1.2.3 KernSmooth_2.23-21 stringi_1.7.12
[65] parallel_4.1.0 Rcpp_1.0.11 vctrs_0.6.3 sf_1.0-14
[69] png_0.1-7 tidyselect_1.2.0 xfun_0.40